Skip to content

[v2] Perf: Use one atom subscription per Field - #2358

Open
scttcper wants to merge 1 commit into
TanStack:alphafrom
scttcper:scttcper/field-subscriptions
Open

[v2] Perf: Use one atom subscription per Field#2358
scttcper wants to merge 1 commit into
TanStack:alphafrom
scttcper:scttcper/field-subscriptions

Conversation

@scttcper

@scttcper scttcper commented Aug 24, 2026

Copy link
Copy Markdown

Each React Field subscribed to the same atom twice, once for value and once for meta. The field atom already preserves state identity when neither changes, so use one whole-state subscription instead.

This cuts active subscriptions from two to one per field with the same render behavior.

Summary by CodeRabbit

  • Bug Fixes

    • Improved form field subscription handling to ensure updates are tracked consistently.
    • Preserved existing field API behavior while reducing redundant subscriptions.
  • Tests

    • Updated subscription coverage to verify that each field uses a single active subscription.

Each React Field subscribed to the same atom once for value and again for meta. The field atom already preserves state identity when neither changes, so one whole-state subscription has the same render behavior.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a39663b-a480-4f99-8fc7-ee3214ee7cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 4ad5a7b and 45ed204.

📒 Files selected for processing (2)
  • packages/react-form/src/ReactForm/fieldSubscriptions.lib.ts
  • packages/react-form/tests/FormGroup.spec.tsx

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The field subscription now observes the complete field state through one subscription. The field-context test description and expected active subscription count reflect this change.

Changes

Field subscription update

Layer / File(s) Summary
Unify field state subscription
packages/react-form/src/ReactForm/fieldSubscriptions.lib.ts, packages/react-form/tests/FormGroup.spec.tsx
useValueFieldSubscription subscribes to full field state and tracks that state for memoization. The test expects one active AppForm field-provider subscription.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 45ed2

This localized performance change reduces each field's atom subscriptions from two to one while preserving the stated render behavior; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: lecarbonator

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change and motivation but omits the required Changes, Checklist, and Release Impact sections. Add the required template sections, complete the checklist, and indicate whether a changeset is required.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main performance change: reducing Field atom subscriptions from two to one.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@scttcper
scttcper marked this pull request as ready for review August 24, 2026 17:35
@LeCarbonator

Copy link
Copy Markdown
Contributor

You mention performance for the PR. Do you have some rough numbers for it? Memory / runtime?

@scttcper

Copy link
Copy Markdown
Author

@LeCarbonator all the perf is just from going from 2 to 1

I ran a focused benchmark using the exact before/after hooks with production React 19.2.8. Each variant ran in a fresh process 10 times, alternating order; results are medians.

1,000 mounted fields

Metric Before After Change
Active subscriptions 2,000 1,000 −50%
Retained heap 7.50 MiB 4.39 MiB −41.5%
Mount time 5.39 ms 4.99 ms −7.4%
Update all fields 1.08 ms 0.64 ms −40.1%
Update one field 4.8µs 4.4µs −9%
Renders per update 1 1 No change

The harness mounts non-array fields backed by individual TanStack Store atoms, measures synchronous mount/update work, and measures retained heap after forced GC.

This isolates subscription overhead rather than measuring a complete application, so the absolute timings are directional. The consistent result is half the subscriptions and callbacks, with roughly 3.2 KiB less retained heap per mounted field.

@crutchcorn

Copy link
Copy Markdown
Member

Sorry, don't have a ton of cycles here to respond more in depth, but I'd attempted this in v1 and benchmarks showed MASSIVE improvements then (like 10x), but the UX of 1000 mounted fields slowed to a crawl for reasons I couldn't identify.

Might be helpful for us to all sit down and discuss and on our end manually test.

Not saying that this is a bad PR or approach, just flagging that benchmarks for this has been directionally wrong before.

@scttcper

Copy link
Copy Markdown
Author

Let sol on ultra try to figure out what you were referring to, obviously feel free to review/close whatever.

Model used: OpenAI Codex — GPT-5.6 Sol

Codex investigated the relevant v1 history and benchmarked the exact base and PR commits in production Chromium.

The closest v1 match is PR #2036. That implementation replaced seven subscriptions with one selector that created a new seven-property object and used a custom comparator. It was also mixed with substantial core store and notification changes, so the historical results do not isolate subscription consolidation as the cause. Its “1,000-field” benchmarks actually rendered 100 fields in jsdom and did not measure browser rendering or memory.

V2 has a different design. Before this PR, a normal field subscribes separately to state.value and state.meta. The PR subscribes once to the existing {value, meta} snapshot. V2 core preserves that snapshot’s identity whenever both references are unchanged:

if (prev?.meta === meta && prev.value === value) {
  return prev
}

Therefore, the rerender conditions are equivalent:

before: value changed OR meta changed
after:  field snapshot changed

The PR does not create a projected object, add a custom comparator, or change core notification behavior. ArrayField is also untouched.

Real-browser confirmation with 1,000 controlled fields and 60 fresh contexts per variant found:

  • Mount: 7.5% faster
  • Typing to commit: 12.2% faster
  • Typing p95: 12.7% faster
  • JavaScript heap: 15.4% lower

@crutchcorn

Copy link
Copy Markdown
Member

@scttcper I'm an idiot and I wasted your tokens.

I just reviewed the diff quickly. For some reason I'd gotten a LOT wrong in my mind about what this PR was and assumed it was doing something more closely related to splitting one store into many for modularity. Frankly I don't know how I got things this mixed up.

So sorry.

This PR LGTM broadly, but might have weird consequences with Compiler. So long as it works well there we should merge.

Sorry again, that's embarrassing.

@scttcper

scttcper commented Aug 27, 2026

Copy link
Copy Markdown
Author

@crutchcorn work pays for it 🤷

@LeCarbonator

Copy link
Copy Markdown
Contributor

I think this should be compiler safe. Don't see how changing it from two to one could have consequences for it.

I wish I had better Wi-Fi to actually try this, but that'll have to wait until next monday.

@nx-cloud

nx-cloud Bot commented Aug 27, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 45ed204

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 4m View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 4s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-27 21:06:00 UTC

@pkg-pr-new

pkg-pr-new Bot commented Aug 27, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@2358

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@2358

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@2358

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@2358

@tanstack/preact-form

npm i https://pkg.pr.new/@tanstack/preact-form@2358

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@2358

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@2358

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@2358

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@2358

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@2358

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@2358

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@2358

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@2358

commit: 45ed204

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (alpha@4ad5a7b). Learn more about missing BASE report.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff            @@
##             alpha    #2358   +/-   ##
========================================
  Coverage         ?   92.91%           
========================================
  Files            ?       14           
  Lines            ?      240           
  Branches         ?       19           
========================================
  Hits             ?      223           
  Misses           ?       16           
  Partials         ?        1           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants